// 版权所有2011 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package binary

// 此文件实现64位整数的“可变”编码。
// 编码为：
// -无符号整数一次序列化7位，从
// 最低有效位
// -每个输出字节中的最高有效位（msb）指示是否存在
// 是一个连续字节（msb=1）
// -使用“之字形”
// 编码将有符号整数映射到无符号整数：正值x写为2*x+0，负值
// 写为2*（^x）+1；也就是说，负数被补码
// 并且是否补码被编码在位0中。
// 
// 设计说明：
// 64位值最多需要10个字节。编码可能会更加密集：一个完整的64位值需要一个额外的字节来容纳第63位。
// 相反，前一个字节的msb可以用来保存位63，因为我们知道不能超过64位。这是一个微不足道的改进，
// 将最大编码长度减少到9字节。然而，它打破了
// 的不变，即msb始终是“连续位”，从而使
// 格式与较大数字的可变编码（例如128位）不兼容。

import (
	"errors"
	"io"
)

// MaxVarinternn是变量编码的N位整数的最大长度。
const (
	MaxVarintLen16 = 3
	MaxVarintLen32 = 5
	MaxVarintLen64 = 10
)

// PutUvarint将uint64编码为buf，并返回写入的字节数。
// 如果缓冲区太小，PutUvarint会惊慌失措。
func PutUvarint(buf []byte, x uint64) int {
	i := 0
	for x >= 0x80 {
		buf[i] = byte(x) | 0x80
		x >>= 7
		i++
	}
	buf[i] = byte(x)
	return i + 1
}

// Uvarint从buf解码uint64并返回该值和
// 读取的字节数（>0）。如果发生错误，则值为0 
// 字节数n为<=0意味着：
// 
// n==0:buf太小
// n<0:value大于64位（溢出）
// 和-n是读取的字节数
// 
func Uvarint(buf []byte) (uint64, int) {
	var x uint64
	var s uint
	for i, b := range buf {
		if i == MaxVarintLen64 {
			// 捕获超过MaxVarintern64的字节读取。
			// 请参阅问题https:
			return 0, -(i + 1) // 溢出
		}
		if b < 0x80 {
			if i == MaxVarintLen64-1 && b > 1 {
				return 0, -(i + 1) // 溢出
			}
			return x | uint64(b)<<s, i + 1
		}
		x |= uint64(b&0x7f) << s
		s += 7
	}
	return 0, 0
}

// PutVarint将int64编码为buf并返回写入的字节数。
// 如果缓冲区太小，PutVarint会死机。
func PutVarint(buf []byte, x int64) int {
	ux := uint64(x) << 1
	if x < 0 {
		ux = ^ux
	}
	return PutUvarint(buf, ux)
}

// Varint从buf解码int64并返回该值和
// 读取的字节数（>0）。如果发生错误，值为0 
// 字节数n为<=0，其含义如下：
// 
// n==0:buf太小
// n<0:value大于64位（溢出）
// 和-n是读取的字节数
// 
func Varint(buf []byte) (int64, int) {
	ux, n := Uvarint(buf) // 确定在出现错误时继续
	x := int64(ux >> 1)
	if ux&1 != 0 {
		x = ^x
	}
	return x, n
}

var overflow = errors.New("binary: varint overflows a 64-bit integer")

// 读取从r中读取编码的无符号整数，并将其作为uint64返回。
func ReadUvarint(r io.ByteReader) (uint64, error) {
	var x uint64
	var s uint
	for i := 0; i < MaxVarintLen64; i++ {
		b, err := r.ReadByte()
		if err != nil {
			return x, err
		}
		if b < 0x80 {
			if i == MaxVarintLen64-1 && b > 1 {
				return x, overflow
			}
			return x | uint64(b)<<s, nil
		}
		x |= uint64(b&0x7f) << s
		s += 7
	}
	return x, overflow
}

// ReadVarint从r中读取编码的有符号整数，并将其作为int64返回。
func ReadVarint(r io.ByteReader) (int64, error) {
	ux, err := ReadUvarint(r) // 如果出现错误，是否继续
	x := int64(ux >> 1)
	if ux&1 != 0 {
		x = ^x
	}
	return x, err
}
